#include "opencv2/opencv.hpp"
#include "boost/filesystem.hpp"
#include "boost/property_tree/xml_parser.hpp"
#include "boost/foreach.hpp"
#include "../beacon.es/json.hpp"
#include <sstream>
#include <fstream>
#include <map>

using namespace std;
using namespace cv;
using nlohmann::json;

// split string
static std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
	std::stringstream ss(s);
	std::string item;
	elems.clear();
	while (std::getline(ss, item, delim)) {
		elems.push_back(item);
	}
	return elems;
}

void loadBeaconAddress(std::map<int, std::string> &beacon_addrs, std::string beacons_fn);
void addMapInfo(nlohmann::json &info_node, boost::filesystem::path p_root, std::string map_name, 
	std::map<int, std::string> &beacon_addrs);

int main(int argc, char **argv)
{
	string work_dir = argc > 1 ? argv[1] : "";
	boost::filesystem::path p_root(work_dir);

	string maps_str = argc > 2 ? argv[2] : "";
	vector<string> map_names;
	split(maps_str, ';', map_names);

	string beacons_fn = argc > 3 ? argv[3] : "";
	map<int, string> beacon_addrs;
	loadBeaconAddress(beacon_addrs, (p_root / beacons_fn).string());

	string info_fn = argc > 4 ? argv[4] : "";

	json info_node;
	for (int i = 0; i < map_names.size(); ++i) {
		addMapInfo(info_node, p_root, map_names[i], beacon_addrs);
	}

	info_node["received"]["file"] = "test.json";

	string dump_fn = (p_root / info_fn).string();
	ofstream ofs(dump_fn);
	ofs << setw(4);
	ofs << info_node;
	ofs.close();
}

void loadBeaconAddress(std::map<int, std::string> &beacon_addrs, std::string beacons_fn)
{
	boost::property_tree::ptree pt;
	boost::property_tree::read_xml(beacons_fn, pt);
	boost::property_tree::ptree dict = pt.get_child("plist.dict");

	int last_key = -1;
	BOOST_FOREACH(boost::property_tree::ptree::value_type &v, dict) {
		if (v.first == "key") {
			last_key = v.second.get_value<double>();
			cout << last_key << ": ";
		} else {
			cout << v.second.get_value<string>() << endl;
			beacon_addrs[last_key] = v.second.get_value<string>();
		}
	}
}

void addMapInfo(nlohmann::json &info_node, boost::filesystem::path p_root, std::string map_name,
	std::map<int, std::string> &beacon_addrs)
{
	string sz_fn = (p_root / (map_name + "-config.size.txt")).string();
	ifstream ifs_sz(sz_fn);
	float width, height;
	ifs_sz >> width >> height;
	ifs_sz.close();

	int map_id = info_node["maps"].size() + 1;

	// setup map
	json map_node;
	map_node["width"] = width;
	map_node["height"] = height;
	map_node["map_id"] = map_id;
	map_node["map_name"] = map_name;

	string line_str;
	//map_node["map_name"]["valid_pos"].clear();

	string valid_pos_fn = (p_root / (map_name + "-config.valid_pos.txt")).string();
	ifstream ifs_valid_pos(valid_pos_fn);
	vector<float> pos_xy;
	while (getline(ifs_valid_pos, line_str)) {
		vector<string> num_strs;
		split(line_str, ',', num_strs);

		float pos_x = atof(num_strs[3].c_str());
		float pos_y = atof(num_strs[4].c_str());
		pos_xy.push_back(pos_x);
		pos_xy.push_back(pos_y);
	}
	ifs_valid_pos.close();

	map_node["valid_pos"] = pos_xy;

	info_node["maps"].push_back(map_node);

	// add beacons
	string beacon_fn = (p_root / (map_name + "-config.beacon.txt")).string();
	ifstream ifs_beacon(beacon_fn);
	while (getline(ifs_beacon, line_str)) {
		vector<string> num_strs;
		split(line_str, ',', num_strs);

		int beacon_id = atoi(num_strs[0].c_str());
		float pos_x = atof(num_strs[3].c_str());
		float pos_y = atof(num_strs[4].c_str());

		json beacon_node;
		beacon_node["beaconAddress"] = beacon_addrs[beacon_id];
		beacon_node["pos_x"] = pos_x;
		beacon_node["pos_y"] = pos_y;
		beacon_node["map_id"] = map_id;
		beacon_node["beacon_id"] = beacon_id;

		info_node["beacons"].push_back(beacon_node);
	}
}
